import { callApi } from 'clib/hooks/async/callApi';
import { JWT_TEXT, JWT } from './jwt_example';
import { useEffect, useState } from 'react';
import { get, set } from 'js-cookie';

set('clientOnly', 'jwt', {
  path: '/shared_authinfo_cross_domain',
  secure: true,
});

interface ILoginResp {
  accessToken: string;
  expiresIn: number;
  redirectUrl: null | string;
  refreshToken: string | null;
  scope: null;
  tokenType: "access_token" | "refresh_token";
  urlToken: string | null;
  username: string;
}

const ExampleRefresh = {
  "accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkdWxhb2JhbiIsInBob25lIjoiMTMxMjMzNDU0MzIiLCJ1cmxUb2tlbiI6IjA5MzlsQ2FsIiwib3JnQ29kZSI6MTAwMDE0LCJyb2xlcyI6WyJ1Y2VudGVyI1JPTEVfU1lTIiwiY21wI0NNUF9URU5BTlRfQURNSU4iLCJvc3MjT1NTX0FETUlOIl0sImV4cCI6MTYwNjM3MDg5NywidXVpZCI6ImR1bGFvYmFuIiwiZW1haWwiOiJkdWxhb2JAZW5uLmNiIiwicmVzb3VyY2VDb2RlcyI6W10sInVzZXJuYW1lIjoiZHVsYW9iYW4ifQ.caUScgrPLvcmaWlqZIXclhN5GCSrc9KTPq5N5s1ykKqkOG20QtwQ7AeACjyJyrVZvR-vmDgBvk5QSBxrC8JbBQ",
  "expiresIn": 1606370897006,
  "redirectUrl": null,
  "refreshToken": null,
  "scope": null,
  "tokenType": "refresh_token",
  "urlToken": "0939lCal",
  "username": "dulaoban",
}

/** 持有当前窗口的登录状态 */
let loginState: ILoginResp;

// 必须在靠前定义，后面会用到
type setLoginStateType = React.Dispatch<React.SetStateAction<string>>;
const listeners = [] as setLoginStateType[];

function getUsername() {
  return loginState ? loginState.username : '';
}

const setLoginState: setLoginStateType = () => {
  listeners.forEach(setState => setState(getUsername()));
}

/**
 * 对外提供登录 hook，供应登录状态(登录用户名)
 */
export function useLoginState() {
  const [state, setState] = useState(getUsername);
  useEffect(() => {
    listeners.push(setState);
    return () => {
      const index = listeners.findIndex(item => item === setState);
      listeners.splice(index, 1);
    }
  }, []);

  return state;
}

const appSecret = 'hyFm(K7&'; // as oss
const SharedUsernameKey = 'enncloud-username';
const SharedLoginResultKey = 'enncloud-login-result';

/**
 * 封装是因为 atob 只支持 ASCII，往后若需要支持中文等字符集，实现方式需要修改
 * @param base64Text 带转换的 base64 编码字符串
 */
function base64Decode(base64Text: string) {
  return atob(base64Text);
}
// console.log('decoded', atob(JWT_TEXT))

/**
 * 将JWT字符串中的内容解析到js数据
 * @param jwtText 带提取内容到js数据的jwt原始文本
 */
function jwtDecode(jwtText: string) {
  const jwtConent = jwtText.split('.')[1];
  return JSON.parse(base64Decode(jwtConent));
}

// console.log('jwtDecode', jwtDecode(JWT_TEXT)); // 用真实的 JWT 测试 jwtDecode

/**
 * 用户名密码登录，获取认证凭证信息
 * @param username 用户名
 * @param password 密码
 */
export async function login(username: string, password: string) {
  if (hdRefresh) {
    clearInterval(hdRefresh);
  }
  return callApi(`!post /ucenter/api/auth/token`, {
    username,
    password,
    appSecret, // 可能后台不看，可以去掉
  }).then(resp => {
    console.dir(resp);
    if (!resp.errorCode) { // 成功登录
      afterLoggedIn(resp.content, false);
      // setTimeout(refresh, 5000); // 测试登录完马上 refreshToken
      const jwtContent: JWT = jwtDecode(loginState.accessToken);
      // if (jwtContent.exp !== loginResp.expiresIn) {
      //   console.error('exp !=', jwtContent.exp, loginResp.expiresIn);
      //   // 结果 exp != 1606393718 1606393718048，所以 jwt 里面的精准
      // }
      return jwtContent;
      // setUsername(content.username);
    } else { // 失败登录
      throw resp;
    }
  });
}

export function logout() {
  loginState = undefined;
  setLoginState(loginState);
  localStorage.removeItem(SharedUsernameKey);
  localStorage.removeItem(SharedLoginResultKey); // 新开子应用可以取出还在有效期可以当做已经登录完
  window.name = '';
}

/**
 * 使用 refreshToken 更新 accessToken
 */
function refresh() {
  callApi('!post /api/auth/token', {
    tokenType: 'refresh_token',
    refreshToken: loginState.refreshToken,
  }).then((resp) => {
    // console.log('refresh', resp);
    if (!resp.errorCode) { // 成功登录
      loginState = resp.content;
      // refresh 因为用户不变，不更新共享区的 username
      // 但是要更新共享区的登录结果，便于其他 window 新开本子应用可以直接登录进去
      updateSharedLoginResult();
    }
  });
}


let hdRefresh: number; // setInterval 返回的 handler
const DEFAULT_REFRESH_INTERVAL = 20 * 60 * 1000; // 默认 20 分钟刷新 JWT

/**
 * 启动定时任务更新 accessToken
 */
function startRefreshAtInterval() {
  if (hdRefresh) {
    clearInterval(hdRefresh);
  }
  setInterval(refresh, DEFAULT_REFRESH_INTERVAL);
}

/**
 * 更新共享的登录用户，用于比较是否切用户了
 */
function updateSharedUsername() {
  localStorage.setItem(SharedUsernameKey, loginState.username);
}

/**
 * 更新共享的登录或刷新结果，用于新窗口从share区取出或从 opener.name 中取出，方便直接登录
 */
function updateSharedLoginResult() {
  console.log('updateSharedLoginResult', loginState.accessToken);
  const text = JSON.stringify(loginState);
  localStorage.setItem(SharedLoginResultKey, text); // 新开子应用可以取出还在有效期可以当做已经登录完
  window.name = text; // 链接到的 window 用 opener.name 可以取出
}

/**
 * 校验登录响应数据是否还在有效期，不在抛出异常。只在窗口加载时有继承的会话时检查。
 * @param loginResult 登录响应内容
 */
function checkLoginResult(loginResult: ILoginResp) {
  console.dir(loginResult);
  const jwtContent: JWT = jwtDecode(loginResult.accessToken);
  console.dir(jwtContent);
  if (Date.now() > jwtContent.exp * 1000) {

    console.log(Date.now(), jwtContent.exp);
    alert('登录凭证超过有效期！');
    throw new Error('登录凭证超过有效期！');
  }
}

/**
 *
 * @param loginResult 某个登录响应
 * @param isPorted 是否是带过来的，不是用户名密码登录过来的
 */
function afterLoggedIn(loginResult: ILoginResp, isPorted: boolean) {
  // 先校验有效期，不在有效期删除并且引发异常
  if (isPorted) {
    checkLoginResult(loginResult);
  }
  loginState = loginResult; // 相当于和正常登录完一样的效果
  setLoginState(loginState);
  // window.loginState = loginState; // debug 用
  updateSharedLoginResult();
  updateSharedUsername();
  startRefreshAtInterval();
  if (isPorted) {
    refresh(); // 因为可以传递过来时，马上就要过期需要刷新了，等不到标准刷新周期了
  }
}


(function bootstrapClosure() {
  /**
  * 应用刚刚启动
  */
  function bootstrap() {
    callApi.getToken = () => {
      console.log('callApi.getToken', loginState);
      return (loginState ? loginState.accessToken : '');
    };
    // 新标签也调转
    if (window.opener) {
      try {
        const loginResult = JSON.parse(window.opener.name);
        afterLoggedIn(loginResult, true);
        alert('窗口内调转直接链接带着认证信息')
        return;
      } catch (e) {
        // 不是来自云平台的链接过来的，正常如
      }
    }
    // 相同窗口内调转
    if (window.name) {
      try {
        const loginResult = JSON.parse(window.name);
        afterLoggedIn(loginResult, true);
        alert('调转到新窗口直接链接带着认证信息')
        return;
      } catch (e) {
        // 不是来自云平台的链接过来的，正常如
      }
    }
    try {
      const loginResult = JSON.parse(localStorage.getItem(SharedLoginResultKey) || '');
      if (loginResult) {
        // 没有链接过来但是
        // debugger;
        afterLoggedIn(loginResult, true);
        alert('非跳转进入，必须进入首页！');
        // window.location.replace('/'); // 转到首页，防止任意粘贴 url 到地址栏
        return;
      }
    } catch (e) {
      // 没有可用的共享区登录结果，just ignore it
    }
  }
  bootstrap();
})();

(function checkChangeClosure() {
  /**
   * 检查本应用是否在其他 window 被注销或以其他用户身份登录了，若是提示并强制关闭窗口
   */
  function checkChange() {
    if (!loginState) return; // 没登录过，无需检查
    const sharedUsername = localStorage.getItem(SharedUsernameKey) || '';
    let tip = '';
    if (!sharedUsername) {
      tip = `其他窗口或者其他标签页已经从本应用注销，本页面不能再继续使用！`;
    } else if (sharedUsername !== loginState.username) {
      tip = `其他窗口或者其他标签页使用其他用户登录了本应用，本页面不能再继续使用！`;
      // console.log(sharedUsername, loginState.username);
    }
    if (tip) {
      clearInterval(hdCheck); // 已经检查出问题，不再继续做周期性检查了
      alert(tip);
      window.close();
      window.location.replace('/');
    }
  }

  /** 默认 5s 检查一次是否被其他 window 注销或者用其他用户登录 */
  const DEFAULT_CHECK_INTERVAL = 5 * 1000;

  let hdCheck: number; // setInterval 返回的 handler
  /**
   * 启动定时任务更新 accessToken
   */
  function startCheckChangeInterval() {
    hdCheck = setInterval(checkChange, DEFAULT_CHECK_INTERVAL);
  }

  // 自动就启动周期性检查是否被其他 window 注销或者用其他用户登录
  startCheckChangeInterval(); //
})();

/**
 * 获取还有效的 JWT，用于 API 调用带着证明已登录过谁登录的。本模块内部不用。
 * 可能不用，redux 改造直接调用登录接口成功取 token 即可。
 */
function getToken() {

}

